package com.oswift.utils.http;

import java.io.BufferedInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.oswift.utils.common.StreamUtil;
import com.oswift.utils.common.URIEncoderDecoder;
import com.oswift.utils.exception.ErrorCode;
import com.oswift.utils.exception.PlatException;

/**
 *
 * HTTP下载工具类 封装数据，HTTP请求，存储HTTP请求地址、请求方法类型、请求头、请求参数
 *
 * @author zhuou
 * @version C03 Oct 27, 2012
 * @since OSwift GPM V1.0
 */
public final class HttpDownloadUtil
{
    /**
     * 日志对象
     */
    private static Logger log = LoggerFactory.getLogger(HttpDownloadUtil.class);

    private HttpDownloadUtil()
    {
    }

    /**
     * 到远程服务器上下载文件到本地
     *
     * @param remotePath
     *            远程文件路径
     * @param localPath
     *            本地文件路径(包含文件名)
     * @throws PlatException
     *             下载文件失败
     */
    public static void downloadFile(String remotePath, String localPath)
        throws PlatException
    {
        // 判断待下载文件在本地是否存在同名文件，若有则先删除。
        File localFile = new File(localPath);
        if (localFile.exists() && localFile.delete())
        {
            log.warn("The file [" + localFile
                    + "] is exist and deleted successful.");
        }

        // 声明HTTP远程连接
        HttpURLConnection urlconn = null;

        try
        {
            // 对URL进行转义
            // 创建URL对象，并打开远程连接
            urlconn = (HttpURLConnection) new URL(URIEncoderDecoder.encode(
                    "encodeURI", remotePath)).openConnection();
            urlconn.connect();

            // 对待下载的文件做的简单容错校验，若内容长度不合法，则不执行下载操作。
            String contentLength = urlconn.getHeaderField("Content-Length");
            checkContentLength(contentLength, remotePath);

            doDownload(urlconn, localFile);
        }
        catch (IOException e)
        {
            log.error("Fail to download file from [" + remotePath + "] to ["
                    + localPath + "].");
            throw new PlatException(ErrorCode.COMMON_DOWNLOAD_REMOTE_FILE_FAIL,
                    e);
        }
        finally
        {
            StreamUtil.getInstance().disconnect(urlconn);
        }
    }

    /**
     * 检查内容长度的基本有效性
     *
     * @param contentLength
     *            内容长度
     * @param remotePath
     *            待下载文件路径
     * @throws PlatException
     *             通用异常
     */
    private static void checkContentLength(String contentLength,
            String remotePath) throws PlatException
    {
        if (null == contentLength || "".equals(contentLength)
                || "0".equals(contentLength))
        {
            String errMsg = "The content length:" + contentLength
                    + " is invalid, the file to be downloaded is " + remotePath;
            log.error(errMsg);
            throw new PlatException(errMsg);
        }
    }

    /**
     * 执行下载过程中的流操作
     *
     * @param urlconn
     *            URL连接
     * @param localFile
     *            本地文件
     * @throws PlatException
     *             流操作异常
     */
    private static void doDownload(HttpURLConnection urlconn, File localFile)
        throws PlatException
    {
        DataOutputStream dos = null;
        BufferedInputStream bis = null;
        FileOutputStream fos = null;

        // 创建输出流
        try
        {
            fos = new FileOutputStream(localFile);
            dos = new DataOutputStream(fos);

            // 创建输入流
            bis = new BufferedInputStream(urlconn.getInputStream());

            // 以20k字节数组作为缓冲区
            final int initSize = 20480;
            byte[] bt = new byte[initSize];
            int len = 0;
            while ((len = bis.read(bt)) > 0)
            {
                // 写文件
                dos.write(bt, 0, len);
            }

            dos.flush();
        }
        catch (IOException e)
        {
            log.error("Write local file [" + localFile + "] failed.");
            throw new PlatException(ErrorCode.COMMON_DOWNLOAD_REMOTE_FILE_FAIL,
                    e);
        }
        finally
        {
            StreamUtil.getInstance().close(bis, dos, fos);
        }
    }
}
